<?php
/**
 * テンプレート管理モジュール Template
 * 基本的にAll用モジュール
 */
class Template {
	public  $Smarty;	// Smartyのインスタンス

	private $CallFlg;
	private $TemplateTable;	// テンプレートデータのテーブル
	private $Cache;			// テンプレートデータのキャッシュ

	private $AgentType;		// AgentTypeの保管

	public function __construct () {
		$this->CallFlg = false;
	}

	/**
	 * Smarty実体化、プリフィルタ登録
	 */
	public function init () {
		Y('this.Admin+', YuTori::Create($this, 'callAdmin'));

		// Smarty実体化
		if (!defined ('SMARTY_DIR')) define ('SMARTY_DIR', DIR_BASE_SYS.'/lib/vendor/Smarty/libs/');
		require_once(SMARTY_DIR . 'Smarty.class.php');

		// Smartyの定義
		$this->Smarty = new Smarty();

		$this->AgentType = strtolower(Y('Request.AgentType'));
		Y('Logger.Debug', 'AgentType: '.$this->AgentType);

		// varsディレクトリのチェック
		if (!is_dir (DIR_BASE_SYS.'/vars/mod/template/'.$this->AgentType)) {
			if (!@mkdir (DIR_BASE_SYS.'/vars/mod/template/'.$this->AgentType, 0777, true)) {
				throw new YuToriException ('モジュールディレクトリの作成に失敗 : '. DIR_BASE_SYS.'/vars/mod/template/'.$this->AgentType);
			}
		}
		
		$this->Smarty->template_dir = DIR_BASE_SYS;

		// コンパイルディレクトリをAgentTypeによって切り替える
		$this->Smarty->compile_dir  = DIR_BASE_SYS.'/vars/mod/template/'.$this->AgentType;

		// デフォルトはGOリソース
		$this->Smarty->default_resource_type = 'go';
		
		// テンプレートテーブルの指定
		$this->TemplateTable = 'template_tpl_table';

		// Y呼び出し用オブジェクトのアサイン
		$this->Smarty->assign_by_ref('YuTori', new Template_Y());

		// リソースプラグインの登録
		$this->Smarty->register_resource('go', array(
			array($this,'smarty_resource_source'),
			array($this,'smarty_resource_timestamp'),
			array($this,'smarty_resource_secure'),
			array($this,'smarty_resource_trusted'),
			)
		);


		// プリフィルタの登録		
		Y ('Output.Template.Filter.Pre.Smarty+', YuTori::Create($this, 'TplOutput'));
		return true;
	}

	/**
	 * 引数trueでcallすると最新ヒストリを表示するテストモードになる。
	 */
	public function call ($Switch = false) {
		if (!$Switch) return;
		if ($this->TemplateTable == 'template_tpl_diff') return;

		// ここまで来た時点でファイルは存在していない。
		Y('Logger.Debug', 'テストモード開始');
		$Mod = Y('Request.Global.Mod');
		
		$NewBase = Y('App.Config.Site.BaseURL').$Mod.'/';
		Y('Logger.Debug', 'ベースURL変更 '.$NewBase);
		Y('App.Config.Site.BaseURL+', $NewBase);
		$newURI = trim(Y('Request.Global.URI'),'/');
		$newURI = substr ($newURI, strlen($Mod));
		$newURI = trim($newURI,'/');
		Y('Request.Global.URI+', $newURI);
		Y('Logger.Debug', 'リクエストURI変更 '.$newURI);
		
		// テンプレートテーブルの変更
		$this->TemplateTable = 'template_tpl_diff';

		// 対象となるファイルが存在している時はそちら優先
		$Request = Y('Request.Global.Act').'/'.Y('Request.Global.Op');
		if ($Request != '/' && is_file(DIR_BASE_HTML.'/'.$Request)) {
			Y('Output.Name', DIR_BASE_HTML.'/'.$Request);
			if (preg_match('/\.php$/i', $Request)) {
				Y('Output.Message', 'テストモードでphpを動作させることはできません。');
			} else {
				Y('Output.Message', file_get_contents(DIR_BASE_HTML.'/'.$Request));
			}
			return true;
		}

		$this->Smarty->compile_dir .= 'preview/';
		$this->Smarty->force_compile = true;
		if (!file_exists ($this->Smarty->compile_dir)) {
			mkdir ($this->Smarty->compile_dir, 0777, true) or die ('ディレクトリ作成に失敗しました。 '.$this->Smarty->compile_dir);
		}

		Y('Request.Global');
		Y('App');
		
		return false;
	}

	/**
	 * テンプレートの有無を調べる
	 */
	private function TplExists ($Target) {
		if (!$this->Cache) {
			Y('Module.load', 'Vars');
			$this->Cache = Y('Module.List.Vars.sys.Cache', 'Template');
		}

		if (isset($this->Cache[$Target])) return $this->Cache[$Target];

		$Query = array (
			'table'=>$this->TemplateTable,
			'query'=>'select',
			'column'=>'UpdateTime',
			'data'=>array('Name'=>$Target, 'Mode'=>$this->Agent),
			'order'=>'UpdateTime DESC',
			'limit'=>1,
			);

		Y('Helper.loadAction', 'ConnectDB', $Query);
		$Res = Y('Helper.List.ConnectDB.com.Result');
		
		if ($Res) {
			$this->Cache[$Target] = $Res['UpdateTime'];
		} else {
			$this->Cache[$Target] = false;
		}
		
		Y('Module.List.Vars.sys.Cache', 'Template', $Target, $this->Cache[$Target]);
		
		return $this->Cache[$Target];
	}


	/**
	 * TPLを出力する
	 */
	public function TplOutput ($Target = null) {
		Y('Logger.Debug', 'Tpl取得開始 : '.$Target);
		if (!$Target) $Target = 'index.html';

		if (false === $Res = $this->TplExists($Target)) {
			return null;
		} else {
			Y('Output$')->TemplateFlg = false;
			Y('Output.Name', $Target);
			return $this->Smarty->fetch($Target);
		}
	}

	/************************************* Smarty Plugin  *************************************/
	public function smarty_resource_source($tpl_name, &$tpl_source, &$smarty) {
		// データベースからテンプレートを取得する。
		$Query = array (
			'table'=>$this->TemplateTable,
			'query'=>'select',
			'column'=>'Header,Template',
			'data'=>array('Name'=>$tpl_name, 'Mode'=>$this->Agent),
			'order'=>'UpdateTime DESC',
			'limit'=>1,
			);

		Y('Helper.loadAction', 'ConnectDB', $Query);
		$Res = Y('Helper.List.ConnectDB.com.Result');
		if ($Res) {
			if ($Res['Header'] = trim($Res['Header'])) {
				$Res['Header'] = str_replace ("'", '\n', $Res['Header']);
				$Res['Template'] = '{$YuTori->call("Output.addHeader", \''.$Res['Header'].'\')}'.$Res['Template'];
			}
			$tpl_source = $Res['Template'];
			return true;
		} else {
			return false;
		}
	}
	
	public function smarty_resource_timestamp($tpl_name, &$tpl_timestamp, &$smarty) {
		$Res = $this->TplExists($tpl_name);
		if ($Res === false) {
			return false;
		} else {
			$tpl_timestamp = $Res;
			return true;
		}
	}
	
	public function smarty_resource_secure($tpl_name, &$smarty) {
		// 全てのテンプレートがセキュアであるとみなします
		return true;
	}
	
	public function smarty_resource_trusted($tpl_name, &$smarty) {
		// テンプレートでは使用しません
	}



	/************************************* admin  *************************************/
	/**
	 * admin動作
	 */
	public function callAdmin () {
		Y('Module.List.Template.getTpl+', YuTori::Create($this, 'getTpl'));
		Y('Module.List.Template.getHistory+', YuTori::Create($this, 'getHistory'));
		Y('Module.List.Template.removeHistory+', YuTori::Create($this, 'removeHistory'));

		Y('Module.loadAction', 'Editor', 'Template', 'edit.yml', Y('Request.Global.Act'), Y('Request.Global.Op'), Y('Document.toObject$'));
		return true;
	}

	/*************************** 以下、Editor用オブジェクト ***************************/
	/**
	 * テンプレートのデータを取得する
	 */
	public function getTpl () {
		if (!$Target = (int) Y('Module.List.Editor.Target')) return false;

		// 基本データの取得
		$Query = array ('query'=>'select',
					'table'=>'template_tpl_table',
					'data'=>array('ID'=>$Target),
			);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$Data = Y('Helper.List.ConnectDB.Com.ResultAll');

		// 差分データの取得
		$Query = array ('query'=>'select',
					'table'=>'template_tpl_diff',
					'data'=>array('TargetID'=>$Target),
					'limit'=>1,
					'order'=>'RegistTime DESC',
			);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$dData = Y('Helper.List.ConnectDB.Com.ResultAll');
		if (count($dData)) $Data[0] = $dData[0];

		// 追加データの取得
		$Query = array ('query'=>'select',
					'table'=>'template_tpl_data',
					'data'=>array('TargetID'=>$Target),
					'limit'=>1,
			);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$extData = Y('Helper.List.ConnectDB.Com.ResultAll');
		if (count($extData)) {
			$Data[0] = array_merge($Data[0], (array)$extData[0]);
		}
		
		$Data[0]['ID'] = $Target;
		Y('Module.List.Editor$')->Data = $Data;
		return true;
	}

	/**
	 * ヒストリ一覧を取得する
	 */
	public function getHistory () {
		if (!$Target = (int) Y('Module.List.Editor.Target')) return false;
		// 基本データの取得
		$Query = array ('query'=>'select',
					'table'=>'template_tpl_table',
					'data'=>array('ID'=>$Target),
			);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$Data = Y('Helper.List.ConnectDB.Com.ResultAll');

		// 追加データの取得
		$Query = array ('query'=>'select',
					'table'=>'template_tpl_data',
					'data'=>array('TargetID'=>$Target),
					'limit'=>1,
			);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$extData = Y('Helper.List.ConnectDB.Com.ResultAll');
		if (count($extData)) {
			$Data[0] = array_merge((array)$extData[0], $Data[0]);
		}
		
		// 差分データの取得
		$Query = array ('query'=>'select',
					'table'=>'template_tpl_diff',
					'data'=>array('TargetID'=>$Target),
					'order'=>'RegistTime DESC',
			);
		Y('Helper.loadAction', 'ConnectDB', $Query);
		$dData = Y('Helper.List.ConnectDB.Com.ResultAll');
		$Data = array_merge($Data, $dData);

		$Data[0]['ID'] = $Target;
		return $Data;
	}

	/**
	 * 古いヒストリの削除
	 */
	public function removeHistory () {
		$Data = Y('Module.List.Editor.Data');
		$Query = array (
			'query'=>'delete',
			'table'=>'template_tpl_diff',
			'data'=>array (
				'Name'=>$Data['Name'],
				'Mode'=>$Data['Mode'],
				'RegistTime'=>time() - (86400 * 30),
			),
			'where'=>'RegistTime<:RegistTime AND ID IN ( SELECT ID FROM ( SELECT ID FROM `template_tpl_table` WHERE Name=:Name AND Mode=:Mode ORDER BY RegistTime LIMIT 10000 OFFSET 20) AS A )'
		);
		// TODO: ここのクエリは少し見直す。
		Y('Helper.loadAction', 'ConnectDB', $Query);
		return true;
	}

}

/**
 * Smartyアサイン用 Y()呼び出し専用オブジェクト
 */
class Template_Y {
	function call () {
		$Args = func_get_args ();
		return call_user_func_array ('Y', $Args);
	}

	function voidcall () {
		$Args = func_get_args ();
		call_user_func_array ('Y', $Args);
		return null;
	}
}
?>